﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="PhpGrammer.cs" company="">
//   
// </copyright>
// <summary>
//   The php grammer.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace IronPhp {
    using System.Reflection.Emit;
    using System.Text.RegularExpressions;

    using Irony.Parsing;

    /// <summary>
    /// The php grammer.
    /// </summary>
    [Language("php", "5.5", "PHP grammar")]
    public class PhpGrammer : Grammar {
        /// <summary>
        /// Initializes a new instance of the <see cref="PhpGrammer"/> class.
        /// <![CDATA[
        /// <program> ::= <statements>
        /// <statements> ::= <simple-statement>* | <structured-statement>*
        /// <simple-statement>
        /// ::= <echo-statement>
        /// ::= <expression-statement>
        /// ::= <jump-statement>
        /// <structured-statement>
        /// ::= <class-declaration>
        /// ::= <function-statement>
        /// ::= <selection-statement>
        /// ::= <declare-statement>
        /// ::= <compound-statement>
        /// ::= <iteration-statement>
        /// <jump-statement>
        /// ::= <continue-statement>
        /// ::= <break-statement>
        /// ::= <return-statement>
        /// <iteration-statement>
        /// ::= <while-statement>
        /// ::= <for-statement>
        /// ::= <do-statement>
        /// ::= <foreach-statement>
        /// <selection-statement>
        /// ::= <switch-statement>
        /// ::= <if-else-statement>
        /// ::= <if-elseif-statement>
        /// ::= <if-statement>
        /// <break-statement>
        /// ::= 'break' ';'
        /// ::= 'break' <expression> ';'
        /// <compound-statement>
        /// ::= '{' <statement-list> '}'
        /// <continue-statement>
        /// ::= 'continue' ';'
        /// ::= 'continue' <expression> ';'
        /// <do-statement>
        /// ::= 'do' <statement> 'while' '(' <expression> ')' ';'
        /// <for-statement>
        /// ::= 'for' '(' <initialization-expression> ';' <control-expression> ';' <iteration-expression> ')' <body-statement>
        /// <if-statement>
        /// ::= 'if' '(' <expression> ')' <statement>
        /// <if-else-statement>
        /// ::= 'if' '(' <expression> ')' <statement> 'else' <statement>
        /// <if-elseif-statement>
        /// ::= 'if' '(' <expression> ')' <statement> <elseif-clause-list>
        /// ::= 'if' '(' <expression> ')' <statement> <elseif-clause-list> <else-clause>
        /// <return-statement>
        /// ::= 'return' <variable-name> ';'
        /// ::= 'return' <expression> ';'
        /// ::= 'return' ';'
        /// <echo-statement>
        /// ::= 'echo' <expression> ';'
        /// ::= 'echo' <string> ';'
        /// <switch-statement>
        /// ::= 'switch' '(' <expression> ')' '{' <case-clause-list> '}'
        /// <while-statement>
        /// ::= 'while' '(' <control-expression> ')' <body-statement>
        /// 
        /// 
        /// 
        /// 
        /// PHP_script ::= Statement* ;
        /// Statement ::=
        ///      Class_def | Interface_def | Method
        ///     | Return | Static_declaration | Global
        ///     | Try | Throw | Eval_expr
        ///     | If | While | Do | For | Foreach
        ///     | Switch | Break | Continue
        ///     | Declare | Nop
        ///     -- Foreign represents a construct from another IR that does not have a
        ///     -- natural representation in this IR.
        ///     | FOREIGN<IR::Node*>
        ///    ;
        ///
        /// {-
        ///     Class and method definitions
        /// -}
        ///
        /// Class_def ::= 
        ///    Class_mod CLASS_NAME extends:CLASS_NAME? implements:INTERFACE_NAME* Member* ;
        /// Class_mod ::= "abstract"? "final"? ;
        ///
        /// Interface_def ::= INTERFACE_NAME extends:INTERFACE_NAME* Member* ;
        ///
        /// Member ::= Method | Attribute ;
        ///
        /// Method ::= Signature Statement*? ;
        /// Signature ::= Method_mod is_ref:"&"? METHOD_NAME Formal_parameter* ;
        /// Method_mod ::= "public"? "protected"? "private"? "static"? "abstract"? "final"? ;
        /// Formal_parameter ::= Type is_ref:"&"? var:Name_with_default ;
        /// Type ::= CLASS_NAME? ;
        ///
        /// Attribute ::= Attr_mod vars:Name_with_default* ;
        /// Attr_mod ::= "public"? "protected"? "private"? "static"? "const"?  ;
        ///
        /// Name_with_default ::= VARIABLE_NAME Expr? ;
        ///
        /// {-
        ///    Statements
        /// -}
        ///
        /// If ::= Expr iftrue:Statement* iffalse:Statement* ;
        /// While ::= Expr Statement* ;
        /// Do ::= Statement* Expr ;
        /// For ::= init:Expr? cond:Expr? incr:Expr? Statement* ;
        /// Foreach ::= Expr key:Variable? is_ref:"&"? val:Variable Statement* ;
        ///
        /// Switch ::= Expr Switch_case* ;
        /// Switch_case ::= Expr? Statement* ;
        /// Break ::= Expr? ;
        /// Continue ::= Expr? ;
        /// Return ::= Expr? ;
        ///
        /// Static_declaration ::= vars:Name_with_default* ;
        /// Global ::= Variable_name* ;
        ///
        /// Declare ::= Directive+ Statement* ;
        /// Directive ::= DIRECTIVE_NAME Expr ;
        ///
        /// Try ::= Statement* catches:Catch* ;
        /// Catch ::= CLASS_NAME VARIABLE_NAME Statement* ;
        /// Throw ::= Expr ;
        ///
        /// Eval_expr ::= Expr ;
        ///
        /// Nop ::= ;
        ///
        ///
        /// -- Foreign represents a construct from another IR that does not have a natural
        /// -- representation in this IR.
        ///
        ///
        /// {-
        ///    Expressions
        /// -}
        ///
        /// Expr ::=
        ///      Assignment 
        ///     | Cast | Unary_op | Bin_op 
        ///     | Constant | Instanceof
        ///     | Variable | Pre_op 
        ///     | Method_invocation | New 
        ///     | Literal 
        ///     | Op_assignment | List_assignment 
        ///     | Post_op | Array | Conditional_expr | Ignore_errors 
        ///     | FOREIGN<IR::Node*>
        ///     ;
        ///
        /// Literal ::= INT<long> | REAL<double> | STRING<String*> | BOOL<bool> | NIL<> ;
        ///
        /// Assignment ::= Variable is_ref:"&"? Expr ;
        /// Op_assignment ::= Variable OP Expr ;
        ///
        /// List_assignment ::= List_element?* Expr ;
        /// List_element ::= Variable | Nested_list_elements ;
        /// Nested_list_elements ::= List_element?* ;
        ///
        /// Cast ::= CAST Expr ;
        /// Unary_op ::= OP Expr ;
        /// Bin_op ::= left:Expr OP right:Expr ;
        ///
        /// Conditional_expr ::= cond:Expr iftrue:Expr iffalse:Expr ;
        /// Ignore_errors ::= Expr ;
        ///
        /// Constant ::= CLASS_NAME? CONSTANT_NAME ;
        ///
        /// Instanceof ::= Expr Class_name ;
        ///
        /// Variable ::= Target? Variable_name array_indices:Expr?* ;
        /// Variable_name ::= VARIABLE_NAME | Reflection ;
        /// Reflection ::= Expr ;
        ///
        /// Target ::= Expr | CLASS_NAME ;
        ///
        /// Pre_op ::= OP Variable ;
        /// Post_op ::= Variable OP ;
        ///
        /// Array ::= Array_elem* ;
        /// Array_elem ::= key:Expr? is_ref:"&"? val:Expr ;
        ///
        /// Method_invocation ::= Target? Method_name Actual_parameter* ;
        /// Method_name ::= METHOD_NAME | Reflection ;
        ///
        /// Actual_parameter ::= is_ref:"&"? Expr ;
        ///
        /// New ::= Class_name Actual_parameter* ;
        /// Class_name ::= CLASS_NAME | Reflection ;
        ///
        /// {-
        ///  - Additional structure 
        ///  -}
        ///
        /// Commented_node ::= 
        ///       Member | Statement | Interface_def | Class_def | Switch_case | Catch 
        ///     ;
        ///
        /// Identifier ::=
        ///       INTERFACE_NAME | CLASS_NAME | METHOD_NAME | VARIABLE_NAME 
        ///       | CAST | OP | CONSTANT_NAME
        ///       | DIRECTIVE_NAME 
        ///     ; 
        ///
        /// Source_rep ::= Identifier | Literal ;
        /// 
        /// ]]>
        /// </summary>
        public PhpGrammer() {
            var document = new NonTerminal("document");
            var documentSegment = new NonTerminal("documentSegment");
            var php = new NonTerminal("php");
            var statements = new NonTerminal("statements");
            var simpleStatement = new NonTerminal("simpleStatement");
            var expressionStatement = new NonTerminal("expressionStatement");
            var echoStatement = new NonTerminal("echoStatement");
            var doubleQuotedString = new NonTerminal("doubleQuotedString");
            var singleQuotedString = new NonTerminal("singleQuotedString");
            var notDoubleQuoteExpression = new RegexBasedTerminal("notDoubleQuote", @"[^""]*");
            var notSingleQuoteExpression = new RegexBasedTerminal("notSingleQuote", @"[^']*");
            var notPhpBeforeExpression = new RegexBasedTerminal("notPhp", @"[^(<\?php|<\?)]*");

            var phpBegin = new RegexBasedTerminal("phpExpression", @"<\?[pP]{0,1}[hH]{0,1}[pP]{0,1}");
            var phpEnd = new RegexBasedTerminal("phpExpression", @"\?>{1}");

            var stringRun = new NonTerminal("stringRun");
            var stringSegment = new NonTerminal("stringSegment");
            var stringStatement = new NonTerminal("stringStatement");
            var concatonate = new NonTerminal("concatonate");
            var whiteSpaceCharacters = new NonTerminal("whitespaceCharacters") { Rule = this.ToTerm(" ") | "\t" | "\r" | "\n" | "\v" | "\u2085" | "\u2028" | "\u2029" };
            whiteSpaceCharacters.SetFlag(TermFlags.NoAstNode);
            var whiteSpace = new NonTerminal("whiteSpace");
            whiteSpace.Rule = MakeStarRule(whiteSpace, whiteSpaceCharacters);
            whiteSpace.SetFlag(TermFlags.NoAstNode);

            RegisterOperators(1, "||");
            RegisterOperators(2, "&&");
            RegisterOperators(3, "|");
            RegisterOperators(4, "^");
            RegisterOperators(5, "&");
            RegisterOperators(6, "==", "!=");
            RegisterOperators(7, "<", ">", "<=", ">=", "is", "as");
            RegisterOperators(8, "<<", ">>");
            RegisterOperators(9, "+", "-");
            RegisterOperators(10, "*", "/", "%");
            RegisterOperators(11, ".");
            this.Root = document;
            php.Rule = whiteSpace + phpBegin + whiteSpace + statements + whiteSpace + phpEnd + whiteSpace;
            documentSegment.Rule = whiteSpace + notPhpBeforeExpression + php | whiteSpace + notPhpBeforeExpression + whiteSpace | whiteSpace;
            document.Rule = MakeStarRule(document, documentSegment);

            doubleQuotedString.Rule = whiteSpace + ToTerm("\"") + notDoubleQuoteExpression + "\"" + whiteSpace;
            singleQuotedString.Rule = whiteSpace + ToTerm("\'") + notSingleQuoteExpression + "\'" + whiteSpace;
            concatonate.Rule = whiteSpace + ToTerm(".") + whiteSpace;
            stringSegment.Rule = singleQuotedString | doubleQuotedString;
            stringRun.Rule = MakePlusRule(stringRun, concatonate, stringSegment);
            stringStatement.Rule = stringSegment | stringRun;

            simpleStatement.Rule = echoStatement;
            expressionStatement.Rule = Empty;
            statements.Rule = simpleStatement | expressionStatement;
            echoStatement.Rule = ToTerm("echo") + stringStatement + ";";
            this.Delimiters = ",;(){}"; // important for quick-parse tests
            this.MarkPunctuation(";");
            this.LineTerminators = "\r\n\u2085\u2028\u2029"; // CR, linefeed, nextLine, LineSeparator, paragraphSeparator
            this.WhitespaceChars = string.Empty; // add extra line terminators
        }
    }

        //public PhpGrammer() {
            //PHP_script ::= Statement* ;

            //Statement ::=
            //     Class_def | Interface_def | Method
            //    | Return | Static_declaration | Global
            //    | Try | Throw | Eval_expr
            //    | If | While | Do | For | Foreach
            //    | Switch | Break | Continue
            //    | Declare | Nop
            //    -- Foreign represents a construct from another IR that does not have a
            //    -- natural representation in this IR.
            //    | FOREIGN<IR::Node*>
            //    ;

            //{-
            //    Class and method definitions
            //-}

            //Class_def ::= 
            //   Class_mod CLASS_NAME extends:CLASS_NAME? implements:INTERFACE_NAME* Member* ;
            //Class_mod ::= "abstract"? "final"? ;

            //Interface_def ::= INTERFACE_NAME extends:INTERFACE_NAME* Member* ;

            //Member ::= Method | Attribute ;

            //Method ::= Signature Statement*? ;
            //Signature ::= Method_mod is_ref:"&"? METHOD_NAME Formal_parameter* ;
            //Method_mod ::= "public"? "protected"? "private"? "static"? "abstract"? "final"? ;
            //Formal_parameter ::= Type is_ref:"&"? var:Name_with_default ;
            //Type ::= CLASS_NAME? ;

            //Attribute ::= Attr_mod vars:Name_with_default* ;
            //Attr_mod ::= "public"? "protected"? "private"? "static"? "const"?  ;

            //Name_with_default ::= VARIABLE_NAME Expr? ;

            //{-
            //   Statements
            //-}

            //If ::= Expr iftrue:Statement* iffalse:Statement* ;
            //While ::= Expr Statement* ;
            //Do ::= Statement* Expr ;
            //For ::= init:Expr? cond:Expr? incr:Expr? Statement* ;
            //Foreach ::= Expr key:Variable? is_ref:"&"? val:Variable Statement* ;

            //Switch ::= Expr Switch_case* ;
            //Switch_case ::= Expr? Statement* ;
            //Break ::= Expr? ;
            //Continue ::= Expr? ;
            //Return ::= Expr? ;

            //Static_declaration ::= vars:Name_with_default* ;
            //Global ::= Variable_name* ;

            //Declare ::= Directive+ Statement* ;
            //Directive ::= DIRECTIVE_NAME Expr ;

            //Try ::= Statement* catches:Catch* ;
            //Catch ::= CLASS_NAME VARIABLE_NAME Statement* ;
            //Throw ::= Expr ;

            //Eval_expr ::= Expr ;

            //Nop ::= ;


            //-- Foreign represents a construct from another IR that does not have a natural
            //-- representation in this IR.


            //{-
            //   Expressions
            //-}

            //Expr ::=
            //     Assignment 
            //    | Cast | Unary_op | Bin_op 
            //    | Constant | Instanceof
            //    | Variable | Pre_op 
            //    | Method_invocation | New 
            //    | Literal 
            //    | Op_assignment | List_assignment 
            //    | Post_op | Array | Conditional_expr | Ignore_errors 
            //    | FOREIGN<IR::Node*>
            //    ;

            //Literal ::= INT<long> | REAL<double> | STRING<String*> | BOOL<bool> | NIL<> ;
   
            //Assignment ::= Variable is_ref:"&"? Expr ;
            //Op_assignment ::= Variable OP Expr ;

            //List_assignment ::= List_element?* Expr ;
            //List_element ::= Variable | Nested_list_elements ;
            //Nested_list_elements ::= List_element?* ;

            //Cast ::= CAST Expr ;
            //Unary_op ::= OP Expr ;
            //Bin_op ::= left:Expr OP right:Expr ;

            //Conditional_expr ::= cond:Expr iftrue:Expr iffalse:Expr ;
            //Ignore_errors ::= Expr ;

            //Constant ::= CLASS_NAME? CONSTANT_NAME ;

            //Instanceof ::= Expr Class_name ;

            //Variable ::= Target? Variable_name array_indices:Expr?* ;
            //Variable_name ::= VARIABLE_NAME | Reflection ;
            //Reflection ::= Expr ;

            //Target ::= Expr | CLASS_NAME ;

            //Pre_op ::= OP Variable ;
            //Post_op ::= Variable OP ;

            //Array ::= Array_elem* ;
            //Array_elem ::= key:Expr? is_ref:"&"? val:Expr ;

            //Method_invocation ::= Target? Method_name Actual_parameter* ;
            //Method_name ::= METHOD_NAME | Reflection ;

            //Actual_parameter ::= is_ref:"&"? Expr ;

            //New ::= Class_name Actual_parameter* ;
            //Class_name ::= CLASS_NAME | Reflection ;

            //{-
            // - Additional structure 
            // -}

            //Commented_node ::= 
            //      Member | Statement | Interface_def | Class_def | Switch_case | Catch 
            //    ;

            //var identifier = new Terminal("identifier");
            //Identifier ::=
            //      INTERFACE_NAME | CLASS_NAME | METHOD_NAME | VARIABLE_NAME 
            //      | CAST | OP | CONSTANT_NAME
            //      | DIRECTIVE_NAME 
            //    ; 

           // var sourceRep = new Terminal("sourceRep");
            //sourceRep = 
            //Source_rep ::= Identifier | Literal ;
       // }
    }